#ifndef SHERIFFOS2_DR16_HPP
#define SHERIFFOS2_DR16_HPP

#include <array>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <connection_guard.h>
#include <interface.h>
#include <units.h>

using namespace units::literals;

namespace dr16 {

/**
 * @brief 遥控器通道编号
 */
enum class Channel : uint8_t { kRightX = 0, kRightY = 1, kLeftX = 2, kLeftY = 3, kWheel = 4 };

/**
 * @brief 拨动开关编号
 */
enum class Switch : uint8_t { kLeft = 0, kRight = 1 };

/**
 * @brief 接收器当前连接状态
 */
enum class ReceiverState : uint8_t { kNull = 0, kConnected, kLost, kError };

/**
 * @brief 拨动开关当前状态
 */
enum class SwitchState : uint8_t { kNull = 0, kUp = 1, kDown = 2, kMiddle = 3 };

// 兼容旧宏风格
inline constexpr auto DR16_RIGHT_X = Channel::kRightX;
inline constexpr auto DR16_RIGHT_Y = Channel::kRightY;
inline constexpr auto DR16_LEFT_X = Channel::kLeftX;
inline constexpr auto DR16_LEFT_Y = Channel::kLeftY;
inline constexpr auto DR16_PADDLE_WHEEL = Channel::kWheel;

inline constexpr auto DR16_SWITCH_LEFT = Switch::kLeft;
inline constexpr auto DR16_SWITCH_RIGHT = Switch::kRight;

// 配置参数
inline constexpr int kChannelCount = 5;
inline constexpr int kSwitchCount = 2;
inline constexpr int kChannelOffset = 1024;
inline constexpr int kChannelErrorLimit = 700;
inline constexpr int kRxFrameLength = 18;
inline constexpr int16_t kPaddleTrimValue = -1024;

/**
 * @brief 键盘按键数据（16位压缩）
 */
struct KeyboardData {
    uint16_t w : 1;
    uint16_t s : 1;
    uint16_t a : 1;
    uint16_t d : 1;
    uint16_t shift : 1;
    uint16_t ctrl : 1;
    uint16_t q : 1;
    uint16_t e : 1;
    uint16_t r : 1;
    uint16_t f : 1;
    uint16_t g : 1;
    uint16_t z : 1;
    uint16_t x : 1;
    uint16_t c : 1;
    uint16_t v : 1;
    uint16_t b : 1;
};

/**
 * @brief 解码后单帧 DR16 数据结构体
 */
struct ReceiverPacket {
    struct {
        std::array<int16_t, kChannelCount> channel{};
        std::array<SwitchState, kSwitchCount> switchState{};
    } remote;

    struct {
        int16_t x{};
        int16_t y{};
        int16_t z{};
        uint8_t left{};
        uint8_t right{};
    } mouse;

    KeyboardData keyboard{};
};

/**
 * @brief DR16 接收器驱动类（单例）
 */
class Receiver {
public:
    static Receiver& getInstance() {
        static Receiver instance;
        return instance;
    }

    void decode(uint8_t const* buff, int const length) {
        if (!buff || length != kRxFrameLength) return;

        auto& pkt = buffer_.getWriteSlot();

        pkt.remote.channel[0] = cancelOffset((buff[0] | buff[1] << 8) & 0x07FF);
        pkt.remote.channel[1] = cancelOffset((buff[1] >> 3 | buff[2] << 5) & 0x07FF);
        pkt.remote.channel[2] = cancelOffset((buff[2] >> 6 | buff[3] << 2 | buff[4] << 10) & 0x07FF);
        pkt.remote.channel[3] = cancelOffset((buff[4] >> 1 | buff[5] << 7) & 0x07FF);
        pkt.remote.channel[4] = cancelOffset((buff[16] | buff[17] << 8) & 0x07FF);

        pkt.remote.switchState[0] = static_cast<SwitchState>(buff[5] >> 6 & 0x03);
        pkt.remote.switchState[1] = static_cast<SwitchState>(buff[5] >> 4 & 0x03);

        pkt.mouse.x = static_cast<int16_t>(buff[6] | buff[7] << 8);
        pkt.mouse.y = static_cast<int16_t>(buff[8] | buff[9] << 8);
        pkt.mouse.z = static_cast<int16_t>(buff[10] | buff[11] << 8);
        pkt.mouse.left = buff[12];
        pkt.mouse.right = buff[13];

        uint16_t const keyRaw = static_cast<uint16_t>(buff[14]) | buff[15] << 8;
        std::memcpy(&pkt.keyboard, &keyRaw, sizeof(KeyboardData));

        if (pkt.remote.channel[4] == kPaddleTrimValue) pkt.remote.channel[4] = 0;

        lastUpdateTime_ = os::getTime();

        if (frameDataHasError(pkt)) {
            state_ = ReceiverState::kError;
            reset();
        } else {
            state_ = ReceiverState::kConnected;
            buffer_.commitWrite();
        }
    }

    [[nodiscard]] ReceiverPacket const& getData() const { return buffer_.getData(); }
    [[nodiscard]] ReceiverPacket const& getPreviousData() const { return buffer_.getPreviousData(); }
    [[nodiscard]] ReceiverState getState() const { return state_; }
    [[nodiscard]] bool isConnected() const { return state_ == ReceiverState::kConnected; }
    [[nodiscard]] bool isError() const { return state_ == ReceiverState::kError; }
    [[nodiscard]] bool isLost() const { return state_ == ReceiverState::kLost; }

    [[nodiscard]] int16_t getChannel(Channel ch) const { return getData().remote.channel[static_cast<uint8_t>(ch)]; }
    [[nodiscard]] SwitchState getSwitchState(Switch sw) const {
        return getData().remote.switchState[static_cast<uint8_t>(sw)];
    }
    [[nodiscard]] bool isSwitchDown(Switch const sw) const { return getSwitchState(sw) == SwitchState::kDown; }
    [[nodiscard]] bool isSwitchMiddle(Switch const sw) const { return getSwitchState(sw) == SwitchState::kMiddle; }
    [[nodiscard]] bool isSwitchUp(Switch const sw) const { return getSwitchState(sw) == SwitchState::kUp; }

    void reset() {
        auto& pkt = buffer_.getWriteSlot();
        std::memset(&pkt, 0, sizeof(ReceiverPacket));
    }

    [[nodiscard]] bool isKeyPressed(char const keycode) const { return getKeyState(getData().keyboard, keycode); }

    [[nodiscard]] bool wasKeyPressed(char const keycode) const {
        return getKeyState(getPreviousData().keyboard, keycode);
    }

    [[nodiscard]] bool isKeyDownEdge(char const keycode) const {
        return isKeyPressed(keycode) && !wasKeyPressed(keycode);
    }

    [[nodiscard]] bool isKeyUpEdge(char const keycode) const {
        return !isKeyPressed(keycode) && wasKeyPressed(keycode);
    }

    [[nodiscard]] bool isMousePressedLeft() const { return getData().mouse.left; }

    [[nodiscard]] bool isMouseDownEdgeLeft() const { return getData().mouse.left && !getPreviousData().mouse.left; }

    [[nodiscard]] bool isMouseUpEdgeLeft() const { return !getData().mouse.left && getPreviousData().mouse.left; }

    [[nodiscard]] bool isMousePressedRight() const { return getData().mouse.right; }

    [[nodiscard]] bool isMouseDownEdgeRight() const { return getData().mouse.right && !getPreviousData().mouse.right; }

    [[nodiscard]] bool isMouseUpEdgeRight() const { return !getData().mouse.right && getPreviousData().mouse.right; }

private:
    Receiver() {
        os::ConnectionGuard::addGuard([this] {
            if (os::getTime() - lastUpdateTime_ > 200_ms && state_ == ReceiverState::kConnected)
                state_ = ReceiverState::kLost;
        });
    }

    static bool getKeyState(KeyboardData const& kb, char const keycode) {
        switch (keycode) {
            case 'w':
                return kb.w;
            case 'a':
                return kb.a;
            case 's':
                return kb.s;
            case 'd':
                return kb.d;
            case 'q':
                return kb.q;
            case 'e':
                return kb.e;
            case 'r':
                return kb.r;
            case 'f':
                return kb.f;
            case 'g':
                return kb.g;
            case 'z':
                return kb.z;
            case 'x':
                return kb.x;
            case 'c':
                return kb.c;
            case 'v':
                return kb.v;
            case 'b':
                return kb.b;
            default:
                return false;
        }
    }

    static bool frameDataHasError(ReceiverPacket const& pkt) {
        for (auto const ch : pkt.remote.channel)
            if (std::abs(ch) > kChannelErrorLimit) return true;

        for (auto const sw : pkt.remote.switchState)
            if (sw == SwitchState::kNull) return true;

        return false;
    }

    static int16_t cancelOffset(uint16_t const raw) {
        return static_cast<int16_t>(static_cast<int>(raw) - kChannelOffset);
    }

    ReceiverState state_ = ReceiverState::kNull;
    units::time::second_t lastUpdateTime_{};
    RingBuffer<ReceiverPacket, 3> buffer_;  // Keep 3 frames to analyze edge events
};

inline auto& dr16_instance = Receiver::getInstance();

}  // namespace dr16

#endif  // SHERIFFOS2_DR16_HPP